////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001.
// -------------------------------------------------------------------------
//  File name:   PickObjectTool.cpp
//  Version:     v1.00
//  Created:     18/12/2001 by Timur.
//  Compilers:   Visual C++ 6.0
//  Description: 
// -------------------------------------------------------------------------
//  History:
//       22/12/2010 Refactored by Sergiy Shaykin.
//		29/04/2011 Improve picking so as to support not-having-physicalproxy-objects and vegetation by Jaesik.
// 
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "Viewport.h"
#include "Material/MaterialManager.h"
#include "Terrain/TerrainManager.h"
#include "Terrain/SurfaceType.h"
#include "VegetationMap.h"
#include "Brush/SolidBrushObject.h"
#include "Objects/EntityObject.h"
#include "MaterialPickTool.h"


#define RENDER_MESH_TEST_DISTANCE 0.2f

//////////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNCREATE(CMaterialPickTool,CEditTool)

static IClassDesc* s_ToolClass = NULL;

//////////////////////////////////////////////////////////////////////////
CMaterialPickTool::CMaterialPickTool()
{
	m_pClassDesc = s_ToolClass;
	m_statusText = _T("Left Click To Pick Material");

	m_hitNormal.Set(0,0,0);
	m_hitPos.Set(0,0,0);
	m_Mouse2DPosition = CPoint(0,0);
}

//////////////////////////////////////////////////////////////////////////
CMaterialPickTool::~CMaterialPickTool()
{
	SetMaterial(0);
}

//////////////////////////////////////////////////////////////////////////
bool CMaterialPickTool::MouseCallback( CViewport *view,EMouseEvent event,CPoint &point,int flags )
{
	if (event == eMouseLDown)
	{
		if (m_pMaterial)
		{
			materialHighlightData.SetMaterial(0);
			CMaterial *pMtl = GetIEditor()->GetMaterialManager()->FromIMaterial(m_pMaterial);
			if (pMtl)
			{
				materialHighlightData.SetMaterial(0);
				GetIEditor()->OpenDataBaseLibrary( EDB_TYPE_MATERIAL,pMtl );
				Abort();
				return true;
			}
		}
	}
	else if (event == eMouseMove)
	{
		return OnMouseMove( view,flags,point );
	}
	return true;
}

//////////////////////////////////////////////////////////////////////////
void CMaterialPickTool::Display( DisplayContext &dc )
{
	CPoint mousePoint;
	::GetCursorPos( &mousePoint );

	dc.view->ScreenToClient( &mousePoint );

	Vec3 wp = dc.view->ViewToWorld( mousePoint );

	if (m_pMaterial)
	{
		float color[4] = {1,1,1,1};
		dc.renderer->Draw2dLabel( mousePoint.x+12,mousePoint.y+8,1.2f,color,false,"%s",(const char*)m_displayString );
		//m_renderer->DrawLabelEx( hit.pt+Vec3(0.2f,0,-0.2f),1.2f,color,true,false,"%s",pSurfaceType->GetName() );
	}

	//dc.SetColor( ColorB(255,0,0,255) );
	//if (!m_hitPos.IsZero())
	//dc.DrawPoint( m_hitPos,3 );

	float fScreenScale = dc.view->GetScreenScaleFactor(m_hitPos) * 0.06f;

	dc.DepthTestOff();
	dc.SetColor( ColorB(0,0,255,255) );
	if (!m_hitNormal.IsZero())
	{
		dc.DrawLine( m_hitPos,m_hitPos + m_hitNormal*fScreenScale );

		Vec3 raySrc,rayDir;
		dc.view->ViewToWorldRay( mousePoint,raySrc,rayDir );

		Matrix34 tm;

		Vec3 zAxis = m_hitNormal;
		Vec3 xAxis = rayDir.Cross(zAxis);
		if (!xAxis.IsZero())
		{
			xAxis.Normalize();
			Vec3 yAxis = xAxis.Cross(zAxis).GetNormalized();
			tm.SetFromVectors( xAxis,yAxis,zAxis,m_hitPos );

			dc.PushMatrix( tm );
			dc.DrawCircle( Vec3(0,0,0),0.5f*fScreenScale );
			dc.PopMatrix();
		}
	}
	dc.DepthTestOn();
}

//////////////////////////////////////////////////////////////////////////
bool CMaterialPickTool::OnMouseMove( CViewport *view,UINT nFlags,CPoint point )
{
	view->SetCurrentCursor( STD_CURSOR_HIT );

	m_hitPos.Set(0,0,0);
	m_hitNormal.Set(0,0,0);

	Vec3 vWorldRaySrc;
	Vec3 vWorldRayDir;
	view->ViewToWorldRay( point, vWorldRaySrc, vWorldRayDir );
	vWorldRaySrc = vWorldRaySrc+vWorldRayDir*0.1f;
	vWorldRayDir = vWorldRayDir*10000.0f;
	m_surfaceId = 0;
	IMaterial* pNearestMaterial(NULL);
	IEntity* pNearnestEntity(NULL);

	m_Mouse2DPosition = point;

	EMaterialPickingObjType objType(eMPT_Max);
	const float enoughFarDistance = 1000000000.0f;

	SRayHitInfo nearestHitInfo;
	memset( &nearestHitInfo, 0, sizeof(nearestHitInfo) );
	nearestHitInfo.fDistance = enoughFarDistance;

	FindNearestMatFromTerrain( vWorldRaySrc, vWorldRayDir, pNearestMaterial, nearestHitInfo, objType );	
 	FindNearestMatFromVegetations( vWorldRaySrc, vWorldRayDir, pNearestMaterial, nearestHitInfo, objType );
 	FindNearestMatFromGeneralObjects( vWorldRaySrc, vWorldRayDir, view, pNearestMaterial, nearestHitInfo, objType);
	FindNearestMatFromEntities( vWorldRaySrc, vWorldRayDir, view, pNearestMaterial, nearestHitInfo, objType);
 	FindNearestMatFromSolids( vWorldRaySrc, vWorldRayDir, view, pNearestMaterial, nearestHitInfo, objType );
//FindNearestMatFromDecals( raySrc, rayDir, view, pNearestMaterial, nearestHitInfo, objType );
	
	if( nearestHitInfo.fDistance < enoughFarDistance )
	{	
		SetMaterial( pNearestMaterial, nearestHitInfo.nHitMatID );
		return true;
	}

	SetMaterial( 0 );
	return false;
}


void CMaterialPickTool::FindNearestMatFromGeneralObjects(	
	const Vec3& vWorldRaySrc, 
	const Vec3& vWorldRayDir, 	
	CViewport* view,
	IMaterial*& pOutLastMaterial, 
	SRayHitInfo& outHitInfo, 
	EMaterialPickingObjType& outObjType )
{
	CBaseObjectsArray objects;	
	GetIEditor()->GetObjectManager()->GetObjects(objects);
	for( size_t i = 0; i < objects.size(); ++i	 )
	{
		CBaseObject* object(objects[i]);

		if( object->GetType() == OBJTYPE_SOLID ||
			object->GetType() == OBJTYPE_DECAL ||
			object->GetType() == OBJTYPE_ENTITY )
		{
			continue;
		}

		IStatObj* pStatObj(NULL);
		IRenderNode* pRenderNode(object->GetEngineNode());
		if( pRenderNode == NULL )
			continue;
		pStatObj = pRenderNode->GetEntityStatObj();
		if( pStatObj == NULL )
			continue;

		if( object->GetMaterial() )
		{
			if( !IsMaterialValid(object->GetMaterial()) )
				continue;
		}

		if( RayIntersection( vWorldRaySrc, vWorldRayDir, pRenderNode, NULL, pStatObj, object->GetWorldTM(), outHitInfo, &pOutLastMaterial ) )				
		{
			if( object->GetMaterial() )
				pOutLastMaterial = object->GetMaterial()->GetMatInfo();

			m_surfaceId = outHitInfo.nHitSurfaceID;
			m_hitNormal = outHitInfo.vHitNormal;
			m_hitPos = outHitInfo.vHitPos;

			outObjType = eMPT_StaticObj;
		}
	}
}

void CMaterialPickTool::FindNearestMatFromSolids( 
	const Vec3& vWorldRaySrc, 
	const Vec3& vWorldRayDir, 
	CViewport *view,
	IMaterial*& pOutLastMaterial, 
	SRayHitInfo& outHitInfo, 
	EMaterialPickingObjType& outObjType )
{	
	CBaseObjectsArray objects;	
	GetIEditor()->GetObjectManager()->GetObjects(objects);
	for( size_t i = 0; i < objects.size(); ++i	 )
	{
		CBaseObject* object(objects[i]);

		if( object->GetType() != OBJTYPE_SOLID )
			continue;

		IStatObj* pStatObj(NULL);
		Matrix34A worldTM;
		SBrush* pSolidBrush = NULL;
		if( ((CSolidBrushObject*)object)->GetBrush(pSolidBrush) == false )
			continue;			
		if( pSolidBrush->GetIStatObj(pStatObj) == false )
			continue;
		worldTM = object->GetWorldTM();

		if( object->GetMaterial() )
		{
			if( !IsMaterialValid(object->GetMaterial()) )
				continue;
		}

		if( RayIntersection( vWorldRaySrc, vWorldRayDir, NULL, NULL, pStatObj, worldTM, outHitInfo, &pOutLastMaterial ) )
		{
			if( object->GetMaterial() )
				pOutLastMaterial = object->GetMaterial()->GetMatInfo();

			m_surfaceId = outHitInfo.nHitSurfaceID;
			m_hitNormal = outHitInfo.vHitNormal;
			m_hitPos = outHitInfo.vHitPos;

			outObjType = eMPT_StaticObj;
		}
	}
}


void CMaterialPickTool::FindNearestMatFromDecals( 
		const Vec3& vWorldRaySrc, 
		const Vec3& vWorldRayDir, 
		CViewport *view,
		IMaterial*& pOutLastMaterial, 
		SRayHitInfo& outHitInfo, 
		EMaterialPickingObjType& outObjType )
{
	CBaseObjectsArray objects;	
	GetIEditor()->GetObjectManager()->GetObjects(objects);
	for( size_t i = 0; i < objects.size(); ++i	 )
	{
		CBaseObject* object(objects[i]);

		if( object->GetType() != OBJTYPE_DECAL )
			continue;

		IRenderNode* pRenderNode(object->GetEngineNode());
		AABB localBoundBox;
		pRenderNode->GetLocalBounds(localBoundBox);
		
		Matrix34 worldTM(object->GetWorldTM());
		Matrix34 invWorldTM = worldTM.GetInverted(); 

		Vec3 localRaySrc = invWorldTM.TransformPoint(vWorldRaySrc);
		Vec3 localRayDir = invWorldTM.TransformVector(vWorldRayDir);

		SRayHitInfo hitInfo;
		Vec3 localRayOut;
		if( Intersect::Ray_AABB( localRaySrc, localRayDir, localBoundBox, localRayOut ) )
		{
			float distance(localRaySrc.GetDistance(localRayOut)); 
			if( distance < outHitInfo.fDistance )
			{
				m_hitNormal		= worldTM.GetColumn2().GetNormalized();
				m_hitPos				= worldTM.TransformPoint(localRayOut);
				pOutLastMaterial = object->GetMaterial()->GetMatInfo();
				outObjType = eMPT_StaticObj;
				outHitInfo.fDistance = distance;
				outHitInfo.vHitPos = m_hitPos;
				outHitInfo.vHitNormal = m_hitNormal;
				outHitInfo.nHitMatID = -1;
			}
		}
	}
}

void CMaterialPickTool::FindNearestMatFromEntities( 
	const Vec3& vWorldRaySrc, 
	const Vec3& vWorldRayDir, 	
	CViewport* view,	
	IMaterial*& pOutLastMaterial, 
	SRayHitInfo& outHitInfo, 
	EMaterialPickingObjType& outObjType )
{
	CBaseObjectsArray objects;	
	GetIEditor()->GetObjectManager()->GetObjects(objects);
	for( size_t i = 0; i < objects.size(); ++i	 )
	{
		CBaseObject* object(objects[i]);
		if( object == NULL )
			continue;
		if( object->GetType() != OBJTYPE_ENTITY )
			continue;
		if( object->IsHidden() )
			continue;

		CEntityObject* entity = static_cast<CEntityObject*>(object);

		if( RayIntersection( vWorldRaySrc, vWorldRayDir, NULL, entity->GetIEntity(), NULL, object->GetWorldTM(), outHitInfo, &pOutLastMaterial ) )
		{
			m_surfaceId = outHitInfo.nHitSurfaceID;
			m_hitNormal = outHitInfo.vHitNormal;
			m_hitPos = outHitInfo.vHitPos;
			AssignObjectMaterial( object, outHitInfo, &pOutLastMaterial );
		}
	}
}

void CMaterialPickTool::AssignObjectMaterial( CBaseObject* pObject, const SRayHitInfo& outHitInfo, IMaterial** pOutMaterial )
{
	if( pObject->GetMaterial() )
	{
		if( pObject->GetMaterial()->GetMatInfo() )
		{
			if( pOutMaterial )
			{
				*pOutMaterial = pObject->GetMaterial()->GetMatInfo();
				if( *pOutMaterial )
				{
					if( outHitInfo.nHitMatID >= 0 && (*pOutMaterial)->GetSubMtlCount() > 0 && outHitInfo.nHitMatID < (*pOutMaterial)->GetSubMtlCount() )
						*pOutMaterial = (*pOutMaterial)->GetSubMtl(outHitInfo.nHitMatID);
				}
			}
		}
	}
}

void CMaterialPickTool::FindNearestMatFromVegetations(	
	const Vec3& vWorldRaySrc, 
	const Vec3& vWorldRayDir, 	
	IMaterial*& pOutLastMaterial, 
	SRayHitInfo& outHitInfo, 
	EMaterialPickingObjType& outObjType )
{
	CVegetationMap * vegetationmap = GetIEditor()->GetVegetationMap();
	std::vector<CVegetationInstance*> vegetations;
	vegetationmap->GetAllInstances(vegetations);

	for( size_t i = 0; i < vegetations.size(); ++i )
	{
		CVegetationInstance * vegetation(vegetations[i]);
		if( vegetation == NULL )
			continue;
		if( vegetation->object == NULL )
			continue;
		IStatObj* pStatObj = vegetation->object->GetObject();
		if( pStatObj == NULL )
			continue;

		float fAngle = DEG2RAD( 360.0f/255.0f*vegetation->angle );
		Matrix34A worldTM;
		worldTM.SetRotationZ( fAngle );
		worldTM.SetTranslation( vegetation->pos );

		if( RayIntersection( vWorldRaySrc, vWorldRayDir, NULL, NULL, pStatObj, worldTM, outHitInfo, &pOutLastMaterial ) )
		{
			if( vegetation->object->GetMaterial() )
				pOutLastMaterial = vegetation->object->GetMaterial()->GetMatInfo();

			m_surfaceId = outHitInfo.nHitSurfaceID;
			m_hitNormal = outHitInfo.vHitNormal;
			m_hitPos = outHitInfo.vHitPos;

			outObjType = eMPT_Vegetation;
		}
	}
}


void CMaterialPickTool::FindNearestMatFromTerrain(
	const Vec3& vWorldRaySrc, 
	const Vec3& vWorldRayDir, 
	 IMaterial*& pOutLastMaterial, 
	 SRayHitInfo& outHitInfo, 
	 EMaterialPickingObjType& outObjType )
{	
	int objTypes = ent_terrain;
	// Include obstruct geometry.
	int flags = (geom_colltype_ray|geom_colltype14)<<rwi_colltype_bit | rwi_colltype_any | rwi_ignore_terrain_holes | rwi_stop_at_pierceable;	
	ray_hit hit;
	hit.pCollider = 0;
	int col = gEnv->pPhysicalWorld->RayWorldIntersection( vWorldRaySrc,vWorldRayDir,objTypes,flags,&hit,1 );
	if (col > 0 && hit.dist < outHitInfo.fDistance && hit.bTerrain )
	{
		m_surfaceId = hit.surface_idx;
		m_hitNormal = hit.n;
		m_hitPos = hit.pt;	
		int nTerrainSurfaceType = hit.idmatOrg;
		if (nTerrainSurfaceType >= 0 && nTerrainSurfaceType < CTerrainManager::GetTerrainManager().GetSurfaceTypeCount())
		{
			CSurfaceType *pSurfaceType = CTerrainManager::GetTerrainManager().GetSurfaceTypePtr(nTerrainSurfaceType);
			CString mtlName = pSurfaceType ? pSurfaceType->GetMaterial() : "";
			if (!mtlName.IsEmpty())
			{
				outObjType = eMPT_Terrain;
				pOutLastMaterial = GetIEditor()->Get3DEngine()->GetMaterialManager()->FindMaterial( mtlName );
				outHitInfo.fDistance = hit.dist;
				outHitInfo.nHitMatID = -1;
			}
		}
	}
}


bool CMaterialPickTool::RayIntersection( 
	const Vec3& vWorldRaySrc, 
	const Vec3& vWorldRayDir, 
	CBaseObject* pBaseObject,
	IMaterial** pOutLastMaterial, 
	SRayHitInfo& outHitInfo )
{
	if( pBaseObject == NULL )
		return false;
	IRenderNode* pRenderNode(pBaseObject->GetEngineNode());
	if( pRenderNode == NULL )
		return false;	
	IStatObj* pStatObj(pRenderNode->GetEntityStatObj());		
	if( pStatObj == NULL )
		return false;
	return RayIntersection( vWorldRaySrc, vWorldRayDir, pRenderNode, NULL, pStatObj, pBaseObject->GetWorldTM(), outHitInfo, pOutLastMaterial );
}

bool CMaterialPickTool::RayIntersection( 
	const Vec3& vWorldRaySrc, 
	const Vec3& vWorldRayDir, 
	IRenderNode* pRenderNode,
	IEntity* pEntity,
	IStatObj* pStatObj, 
	const Matrix34A& WorldTM, 
	SRayHitInfo& outHitInfo,
	IMaterial** ppOutLastMaterial )
{
	SRayHitInfo hitInfo;
	bool bRayIntersection = false;
	IMaterial* pMaterial(NULL);

	bRayIntersection = RayIntersection( vWorldRaySrc, vWorldRayDir, pEntity, &pMaterial, WorldTM, hitInfo );
	if( !bRayIntersection )
	{
		bRayIntersection = RayIntersection( vWorldRaySrc, vWorldRayDir, pStatObj, &pMaterial, WorldTM, hitInfo );		
		if( !bRayIntersection )
			bRayIntersection = RayIntersection( vWorldRaySrc, vWorldRayDir, pRenderNode, &pMaterial, WorldTM, hitInfo );
	}	

	if( bRayIntersection )
	{
		hitInfo.fDistance = vWorldRaySrc.GetDistance(hitInfo.vHitPos);
		if( hitInfo.fDistance < outHitInfo.fDistance )
		{
			if( ppOutLastMaterial )
				*ppOutLastMaterial = pMaterial;
			memcpy( &outHitInfo, &hitInfo, sizeof(SRayHitInfo) );
			outHitInfo.vHitNormal.Normalize();
			return true;	
		}
	}
	return false;
}

bool CMaterialPickTool::RayIntersection(
	const Vec3& vWorldRaySrc,
	const Vec3& vWorldRayDir,
	IStatObj* pStatObj,
	IMaterial** ppOutLastMaterial, 
	const Matrix34A& worldTM,
	SRayHitInfo& outHitInfo )
{
	if( pStatObj == NULL )
		return false;

	Vec3 localRaySrc;
	Vec3 localRayDir;	
	if( !RayWorldToLocal( worldTM, vWorldRaySrc, vWorldRayDir, localRaySrc, localRayDir ) )
		return false;

	memset(&outHitInfo,0,sizeof(outHitInfo));
	outHitInfo.inReferencePoint = localRaySrc;
	outHitInfo.inRay				= Ray(localRaySrc,localRayDir);
	outHitInfo.bInFirstHit	= false;
	outHitInfo.bUseCache		= false;

	if( pStatObj->RayIntersection(outHitInfo) )
	{	
		if( outHitInfo.fDistance < 0 )
			return false;
		Vec3 worldPos(worldTM.TransformPoint(outHitInfo.vHitPos));
		float dist = worldPos.GetDistance(vWorldRaySrc);		
		outHitInfo.vHitPos = vWorldRaySrc+vWorldRayDir.GetNormalized()*dist;
		outHitInfo.vHitNormal = worldTM.TransformVector(outHitInfo.vHitNormal);		
		if( ppOutLastMaterial )
		{
			IMaterial* pMaterial = pStatObj->GetMaterial();
			if( pMaterial )
			{
				*ppOutLastMaterial = pMaterial;
				if( outHitInfo.nHitMatID >= 0 )
				{
					if( pMaterial->GetSubMtlCount() > 0 && outHitInfo.nHitMatID < pMaterial->GetSubMtlCount() )
						*ppOutLastMaterial = pMaterial->GetSubMtl(outHitInfo.nHitMatID);
				}
			}
		}
		return true;
	}

	return false;
}

bool CMaterialPickTool::RayIntersection(
	const Vec3& vWorldRaySrc,
	const Vec3& vWorldRayDir,
	IRenderNode* pRenderNode,
	IMaterial** pOutLastMaterial, 
	const Matrix34A& WorldTM,
	SRayHitInfo& outHitInfo )
{
	if(pRenderNode == NULL )
		return false;

	IPhysicalEntity *physics = 0;
	physics = pRenderNode->GetPhysics();
	if( physics == NULL )
		return false;

	if (physics->GetStatus( &pe_status_nparts() ) == 0)
		return false;

	ray_hit hit;
	int col = GetIEditor()->GetSystem()->GetIPhysicalWorld()->RayTraceEntity( physics,vWorldRaySrc,vWorldRayDir,&hit,0,geom_collides);
	if (col <= 0)
		return false;

	outHitInfo.vHitPos = hit.pt;
	outHitInfo.vHitNormal = hit.n;
	outHitInfo.fDistance = hit.dist;
	if( outHitInfo.fDistance < 0 )
		return false;
	outHitInfo.nHitMatID = -1;
	outHitInfo.nHitSurfaceID = hit.surface_idx;
	IMaterial* pMaterial(pRenderNode->GetMaterial());

	if( pMaterial && pOutLastMaterial )
	{
		*pOutLastMaterial = pMaterial;
		if( pMaterial->GetSubMtlCount() > 0 && hit.idmatOrg >= 0 && hit.idmatOrg < pMaterial->GetSubMtlCount() )
		{
			outHitInfo.nHitMatID = hit.idmatOrg;
			*pOutLastMaterial = pMaterial->GetSubMtl(hit.idmatOrg);
		}
	}  

	return true;
}

bool CMaterialPickTool::RayIntersection(
	const Vec3& vWorldRaySrc,
	const Vec3& vWorldRayDir,
	IEntity* pEntity,
	IMaterial** pOutLastMaterial, 
	const Matrix34A& WorldTM,
	SRayHitInfo& outHitInfo )
{
	if( pEntity == NULL )
		return false;

	IPhysicalWorld *pPhysWorld = GetIEditor()->GetSystem()->GetIPhysicalWorld();
	IPhysicalEntity *pPhysics = 0;
	bool bHit = false;

	IMaterial* pMaterial = NULL;
	int nSlotCount = pEntity->GetSlotCount();
	int nCurSlot = 0;

	for( int i = 0; i < 2; )
	{
		if( i == 0 )
		{
			if( nCurSlot >= nSlotCount )
			{
				++i;
				continue;
			}

			SEntitySlotInfo slotInfo;
			if( pEntity->GetSlotInfo( nCurSlot++, slotInfo ) == false )
				continue;

			if( slotInfo.pCharacter )
			{
				pPhysics = slotInfo.pCharacter->GetISkeletonPose()->GetCharacterPhysics();
				IMaterial* pOldMaterial = slotInfo.pCharacter->GetMaterialOverride();
				slotInfo.pCharacter->SetMaterial(NULL);
				if( slotInfo.pCharacter->GetMaterial() )
				{
					pMaterial = slotInfo.pCharacter->GetMaterial();
				}
				else
				{
					pMaterial = pOldMaterial;
				}
				slotInfo.pCharacter->SetMaterial(pOldMaterial);
			}
			else if( slotInfo.pStatObj )
			{
				if( RayIntersection( vWorldRaySrc, vWorldRayDir, slotInfo.pStatObj, pOutLastMaterial, WorldTM*(*slotInfo.pLocalTM), outHitInfo ) )
					return true;
			}
			else
			{
				continue;
			}
		}
		else
		{
			pPhysics = pEntity->GetPhysics();
			++i;
		}
		if( pPhysics == NULL )
			continue;
		int type = pPhysics->GetType();
		if (type == PE_NONE || type == PE_PARTICLE || type == PE_ROPE || type == PE_SOFT)
			continue;
		else if(pPhysics->GetStatus( &pe_status_nparts() ) == 0)
			continue; 
		ray_hit hit;
		bHit = (pPhysWorld->RayTraceEntity( pPhysics,vWorldRaySrc,vWorldRayDir,&hit ) > 0);
		if( !bHit )
			continue;
		outHitInfo.vHitPos = hit.pt;
		outHitInfo.vHitNormal = hit.n;
		outHitInfo.fDistance = hit.dist;
		if( outHitInfo.fDistance < 0 )
		{
			bHit = false;
			continue;
		}
		outHitInfo.nHitSurfaceID = hit.surface_idx;
		outHitInfo.nHitMatID = hit.idmatOrg;
		break;
	}

	if( !bHit )
		return false;

	if( pOutLastMaterial )
	{
		*pOutLastMaterial = pMaterial;
		if( pMaterial )
		{
			if( outHitInfo.nHitMatID >= 0 && pMaterial->GetSubMtlCount() > 0 && outHitInfo.nHitMatID < pMaterial->GetSubMtlCount() )
				*pOutLastMaterial = pMaterial->GetSubMtl(outHitInfo.nHitMatID);
		}
	}

	return true;
}

bool CMaterialPickTool::RayWorldToLocal(
		const Matrix34A& WorldTM,
		const Vec3& vWorldRaySrc,
		const Vec3& vWorldRayDir,
		Vec3& outRaySrc,
		Vec3& outRayDir )
{
	if( !WorldTM.IsValid() )
		return false;
	Matrix34A invertedM(WorldTM.GetInverted());
	if( !invertedM.IsValid() )
		return false;
	outRaySrc = invertedM.TransformPoint(vWorldRaySrc);
	outRayDir = invertedM.TransformVector(vWorldRayDir).GetNormalized();
	return true;
}

//////////////////////////////////////////////////////////////////////////
// Class description.
//////////////////////////////////////////////////////////////////////////
class CMaterialPickTool_ClassDesc : public CRefCountClassDesc
{
	virtual ESystemClassID SystemClassID() { return ESYSTEM_CLASS_EDITTOOL; }
	virtual REFGUID ClassID() {
		// {FD20F6F2-7B87-4349-A5D4-7533538E357F}
		static const GUID guid = { 0xfd20f6f2, 0x7b87, 0x4349, { 0xa5, 0xd4, 0x75, 0x33, 0x53, 0x8e, 0x35, 0x7f } };
		return guid;
	}
	virtual const char* ClassName() { return "EditTool.PickMaterial"; };
	virtual const char* Category() { return "Material"; };
	virtual CRuntimeClass* GetRuntimeClass() { return RUNTIME_CLASS(CMaterialPickTool); }
};

//////////////////////////////////////////////////////////////////////////
void CMaterialPickTool::RegisterTool( CRegistrationContext &rc )
{
	rc.pClassFactory->RegisterClass( s_ToolClass = new CMaterialPickTool_ClassDesc );
}

//////////////////////////////////////////////////////////////////////////
void CMaterialPickTool::SetMaterial( IMaterial *pMaterial,int nSubMtlIt )
{
	m_pMaterialForDisplay = pMaterial;
	materialHighlightData.SetMaterial(m_pMaterialForDisplay);

	if(nSubMtlIt >= 0 && pMaterial)
		pMaterial = pMaterial->GetSafeSubMtl(nSubMtlIt);

	if (pMaterial == m_pMaterial)
		return;

	m_pMaterial = pMaterial;

	m_displayString = "";
	if (pMaterial)
	{
		CString sfTypeName = GetIEditor()->Get3DEngine()->GetMaterialManager()->GetSurfaceType( m_surfaceId,"Material Pick Tool" )->GetName();
		CString sfType;
		sfType.Format( "(%d: %s)",m_surfaceId,(const char*)sfTypeName );

		m_displayString = pMaterial->GetName();
		m_displayString += "\n";
		m_displayString += CString(pMaterial->GetSurfaceType()->GetName()) + " " + sfType;
	}
}

bool CMaterialPickTool::IsMaterialValid( CMaterial* pMateral ) const
{
	if( pMateral == NULL )
		return false;
	return !(pMateral->GetMatInfo()->GetFlags()&MTL_FLAG_NODRAW);
}